#include "EEPROM_module/EEPROM.hpp"

#ifdef DBG_TAG
#undef DBG_TAG
#endif
#ifdef DBG_LVL
#undef DBG_LVL
#endif

#define DBG_TAG     "EEPROM"
#define DBG_LVL     DBG_LOG

extern "C"
{
#include <rtdbg.h>
}

using namespace std;
EEPROM::EEPROM(rt_base_t wp_pin,
               std::string soft_i2c_name,
               uint32_t bytes_per_page,
               uint32_t total_bytes_num,
               EEPROM_identity_code identity_code,
               EEPROM_A2A1A0_mode A2A1A0_mode,
               EEPROM_word_addr_bit word_addr_bit)
{
    uint8_t dev_addr_temp = 0;
    uint8_t dev_addr_temp2 = 0;
    this->wp_pin = wp_pin;
    this->soft_i2c_name = soft_i2c_name;
    this->word_addr_bit = word_addr_bit;
    this->bytes_per_page = bytes_per_page;
    this->total_bytes_num = total_bytes_num;

    dev_addr_temp = identity_code;
    dev_addr_temp2 = A2A1A0_mode;
    dev_addr_temp = (dev_addr_temp << 3);
    dev_addr_temp |= dev_addr_temp2;
    this->basic_device_address = dev_addr_temp;

    this->debug_flag = 0;



    LOG_I("EEPROM information==========================");
    LOG_I("EEPROM basic device addr <0X%X>", this->basic_device_address);
    LOG_I("I2C bus <%s>", soft_i2c_name.c_str());
    LOG_I("Bytes per page <%d>", this->bytes_per_page);
    LOG_I("total bytes num <%d>", this->total_bytes_num);
    LOG_I("EEPROM information end======================");

}

void EEPROM::EEPROM_init_device()
{
    uint32_t i;
    uint8_t buff1[bytes_per_page + 5];
    uint8_t buff2[bytes_per_page + 5];
    uint8_t origin[bytes_per_page + 5];
    //i2c查询
    soft_i2c = (struct rt_i2c_bus_device *) rt_device_find(soft_i2c_name.c_str());
    if (!soft_i2c)
    {
        LOG_E("EEPROM_init_device : I2C bus <%s> must be set in board.h and open I2C soft in rtthread setting ,before using",
              soft_i2c_name.c_str());
        goto end;
    }

    bus_lock = rt_mutex_create("eeprom_bus_lock", RT_IPC_FLAG_PRIO);
    if(!bus_lock)
    {
        LOG_E("eeprom_bus_lock create fail");
        goto end;
    }

    //读10个数，加一，写回去，再读回来，判断，再改回原来的值写回去
    if (EEPROM_read(0, buff1, bytes_per_page + 5) != EEPROM_status_ok)
    {
        LOG_E("first read fail");
        goto end;
    }

    if (debug_flag) for (i = 0; i < bytes_per_page + 5; i++)
        LOG_D("first read buff1[%d] = <%d> ", i, buff1[i]);

    rt_memcpy(origin, buff1, bytes_per_page + 5);
    for (i = 0; i < bytes_per_page + 5; i++)
    {
        srand((unsigned) rt_tick_get());
        buff1[i] = rand() % 256;
        rt_thread_mdelay(1);
    }

    if (debug_flag) for (i = 0; i < bytes_per_page + 5; i++)
        LOG_D("first write buff1[%d] = <%d> ", i, buff1[i]);

    if (EEPROM_write(0, buff1, bytes_per_page + 5) != EEPROM_status_ok)
    {
        LOG_E("first write fail");
        goto end;
    }

    if (EEPROM_read(0, buff2, bytes_per_page + 5) != EEPROM_status_ok)
    {
        LOG_E("second read fail");
        goto end;
    }

    if (debug_flag) for (i = 0; i < bytes_per_page + 5; i++)
        LOG_D("buff1[%d] = <%d> buff2[%d] = <%d>", i, buff1[i], i, buff2[i]);

    for (i = 0; i < bytes_per_page + 5; i++)
        if (buff1[i] != buff2[i])
        {
            LOG_E("EEPROM init fail");
            goto end;
        }

    rt_memcpy(buff1, origin, bytes_per_page + 5);
    if (EEPROM_write(0, buff1, bytes_per_page + 5) != EEPROM_status_ok)
    {
        LOG_E("second write fail");
        goto end;
    }



    LOG_I("EEPROM init ok");
    init_flag = EEPROM_status_ok;
    return;
    end:
    LOG_E("EEPROM init fail");
    init_flag = EEPROM_status_fail;
    return;
}

//MSB模式转换
void EEPROM::EEPROM_transfer_u32_to_4_u8_temp(uint32_t u32)
{
    rt_memset(word_addr_temp, 0, 4);
    switch (word_addr_bit)
    {
    case EEPROM_word_addr_bit_8:
        word_addr_temp[0] |= (u32 >> 0);
        break;
    case EEPROM_word_addr_bit_16:
        word_addr_temp[0] |= (u32 >> 8);
        word_addr_temp[1] |= (u32 >> 0);
        break;
    case EEPROM_word_addr_bit_24:
        word_addr_temp[0] |= (u32 >> 16);
        word_addr_temp[1] |= (u32 >> 8);
        word_addr_temp[2] |= (u32 >> 0);
        break;
    case EEPROM_word_addr_bit_32:
        word_addr_temp[0] |= (u32 >> 24);
        word_addr_temp[1] |= (u32 >> 16);
        word_addr_temp[2] |= (u32 >> 8);
        word_addr_temp[3] |= (u32 >> 0);
        break;
    default:
        break;
    }

}
EEPROM_status EEPROM::EEPROM_write(uint32_t write_addr, uint8_t* write_buff, uint32_t write_size)
{
    rt_size_t flag;
    struct rt_i2c_msg msg[2];
    uint32_t now_page_can_write_num;
    uint32_t write_addr_temp;
    uint32_t now_write_data_size;
    uint32_t buff_offset;

    //判断写数据否为空，该处无法判断写指针是否是write_size，请输入时务必注意，错误将导致程序跑飞
    if (!write_buff)
    {
        LOG_E("EEPROM write fail : write data buff is NULL");
        goto end;
    }
    //判断地址是否超出写范围
    if (total_bytes_num < write_addr)
    {
        LOG_E("EEPROM write fail : write data addr over max");
        goto end;
    }
    //判断剩余是否够写
    if (total_bytes_num - write_addr < write_size)
    {
        LOG_E("EEPROM write fail : write data size over max");
        goto end;
    }

    buff_offset = 0;
    write_addr_temp = write_addr;
    now_write_data_size = write_size;
    while (now_write_data_size > 0)
    {
        //计算当前页还剩多少可写
        now_page_can_write_num = bytes_per_page - write_addr_temp % bytes_per_page;
        //和剩余需要写入数据比较
        if (now_page_can_write_num >= now_write_data_size) now_page_can_write_num = now_write_data_size;
        //计算写入地址
        EEPROM_transfer_u32_to_4_u8_temp(write_addr_temp);//修改word_addr_temp

        if (debug_flag)
        {
            LOG_D("write:EEPROM_word_addr source is <0X%X>", write_addr_temp);
            for (int i = 0; i < 4; i++)
                LOG_D("write:EEPROM_word_addr arry[%d] is <0X%X>", i, word_addr_temp[i]);
            LOG_D("write:now_page_can_write_num  is <%d>", now_page_can_write_num);
            LOG_D("write:now_write_data_size  is <%d>", now_write_data_size);
        }

        msg[0].addr = (rt_uint16_t) basic_device_address; /* 从机地址 */
        msg[0].flags = RT_I2C_WR | RT_I2C_NO_STOP; /* 写标志、不发送结束 */
        msg[0].buf = word_addr_temp; /* 读写数据缓冲区指针　*/
        switch (word_addr_bit)
        {
        case EEPROM_word_addr_bit_8:
            msg[0].len = 1; /* 字地址字节数 */
            break;
        case EEPROM_word_addr_bit_16:
            msg[0].len = 2; /* 字地址字节数 */
            break;
        case EEPROM_word_addr_bit_24:
            msg[0].len = 3; /* 字地址字节数 */
            break;
        case EEPROM_word_addr_bit_32:
            msg[0].len = 4; /* 字地址字节数 */
            break;
        default:
            break;
        }

        msg[1].addr = (rt_uint16_t) basic_device_address; /* 从机地址 */
        msg[1].flags = RT_I2C_WR | RT_I2C_NO_START; /* 写标志、不发送结束 */
        msg[1].buf = write_buff + buff_offset; /* 读写数据缓冲区指针　*/
        msg[1].len = (uint16_t) now_page_can_write_num; /* 读写数据字节数 */

        rt_mutex_take(bus_lock, RT_WAITING_FOREVER);
        rt_pin_write(wp_pin, PIN_LOW);
        flag = rt_i2c_transfer(soft_i2c, msg, 2); //调用I2C设备接口传输数据
        rt_pin_write(wp_pin, PIN_HIGH);
        rt_mutex_release(bus_lock);

        if (flag != 2)
        {
            LOG_E("EEPROM write fail");
            goto end;
        }

        //写buff地址递增
        buff_offset += now_page_can_write_num;
        //写地址递增
        write_addr_temp += now_page_can_write_num;
        //剩余数据递减
        now_write_data_size -= now_page_can_write_num;

        rt_thread_mdelay(6); //延时TWS
    }

    return EEPROM_status_ok;
    end: return EEPROM_status_fail;

}

EEPROM_status EEPROM::EEPROM_read(uint32_t read_addr, uint8_t* read_buff, uint32_t read_size)
{
    rt_size_t flag;
    struct rt_i2c_msg msg[3];
    uint32_t now_page_can_read_num;
    uint32_t read_addr_temp;
    uint32_t now_read_data_size;
    uint32_t buff_offset;

    //判断读数据否为空，该处无法判断读指针是否是read_size，请输入时务必注意，错误将导致程序跑飞
    if (!read_buff)
    {
        LOG_E("EEPROM read fail : read buff is NULL");
        goto end;
    }
    //判断地址是否超出读范围
    if (total_bytes_num < read_addr)
    {
        LOG_E("EEPROM read fail : read addr over max");
        goto end;
    }
    //判断剩余是否够读
    if (total_bytes_num - read_addr < read_size)
    {
        LOG_E("EEPROM read fail : read data size over max");
        goto end;
    }

    buff_offset = 0;
    read_addr_temp = read_addr;
    now_read_data_size = read_size;
    while (now_read_data_size > 0)
    {
        //计算当前页还剩多少可读
        now_page_can_read_num = bytes_per_page - read_addr_temp % bytes_per_page;
        //和剩余需要读取的数据比较
        if (now_page_can_read_num > now_read_data_size) now_page_can_read_num = now_read_data_size;
        //计算读取地址
        EEPROM_transfer_u32_to_4_u8_temp(read_addr_temp);//修改word_addr_temp

        if (debug_flag)
        {
            LOG_D("read:read_addr source is <0X%X>", read_addr);
            for (int i = 0; i < 4; i++)
                LOG_D("read:EEPROM_word_addr arry[%d] is <0X%X>", i, word_addr_temp[i]);
            LOG_D("read:now_page_can_read_num  is <%d>", now_page_can_read_num);
            LOG_D("read:now_read_data_size  is <%d>", now_read_data_size);
        }

        msg[0].addr = (rt_uint16_t) basic_device_address; /* 从机地址 */
        msg[0].flags = RT_I2C_WR | RT_I2C_NO_STOP; /* 写标志、不发送结束 */
        msg[0].buf = word_addr_temp; /* 读写数据缓冲区指针　*/
        switch (word_addr_bit)
        {
        case EEPROM_word_addr_bit_8:
            msg[0].len = 1; /* 字地址字节数 */
            break;
        case EEPROM_word_addr_bit_16:
            msg[0].len = 2; /* 字地址字节数 */
            break;
        case EEPROM_word_addr_bit_24:
            msg[0].len = 3; /* 字地址字节数 */
            break;
        case EEPROM_word_addr_bit_32:
            msg[0].len = 4; /* 字地址字节数 */
            break;
        default:
            break;
        }

        //此处虽然不要求在最后一个返回ACK，但如果不返回ACK 软件I2C的读会出现问题，故此处是多回复给EEPROM一个ACK
        msg[1].addr = (rt_uint16_t) basic_device_address; /* 从机地址 */
        msg[1].flags = RT_I2C_RD; /* 写标志、不发送结束 */
        msg[1].buf = read_buff + buff_offset; /* 读写数据缓冲区指针　*/
        msg[1].len = (uint16_t) now_page_can_read_num; /* 读写数据字节数*/

        rt_mutex_take(bus_lock, RT_WAITING_FOREVER);
        rt_pin_write(wp_pin, PIN_LOW);
        flag = rt_i2c_transfer(soft_i2c, msg, 2); //调用I2C设备接口传输数据
        rt_pin_write(wp_pin, PIN_HIGH);

        rt_mutex_release(bus_lock);


        if (flag != 2)
        {
            LOG_E("EEPROM read fail");
            goto end;
        }

        //读取地址递增
        buff_offset += now_page_can_read_num;
        //写地址递增
        read_addr_temp += now_page_can_read_num;
        //剩余数据递减
        now_read_data_size -= now_page_can_read_num;

        rt_thread_mdelay(3); //延时TWS
    }

    return EEPROM_status_ok;
    end: return EEPROM_status_fail;

}

void EEPROM::EEPROM_open_debug() //打开调试模式
{
    debug_flag = 1;
}
void EEPROM::EEPROM_close_debug() //关闭调试模式
{
    debug_flag = 0;
}

EEPROM_status EEPROM::EEPROM_get_status()
{
    return init_flag;
}

uint32_t EEPROM::EEPROM_get_bytes_per_page()
{
    return bytes_per_page;
}

uint32_t EEPROM::EEPROM_get_total_bytes_num()
{
    return total_bytes_num;
}

uint32_t EEPROM::EEPROM_get_total_pages_num()
{
    return total_bytes_num/bytes_per_page;
}

